#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

#include <sys/wait.h>
#include <string.h>

#include <signal.h>

#include <sys/types.h>

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<string.h>
#include <errno.h>



#define MAXLEN 128			


#define HIS_LEN 10			




typedef struct history		

{

	int i;
	int start;

	int end;

	char cmd[HIS_LEN][MAXLEN];

}  history;



typedef struct node{		



	pid_t pid;

	

    char cmd[100];



	char state[10];



	struct node *next;



}node;



/*****************************************************************/

node *head,*end;		

history his;			

char buf[MAXLEN];		

char *path[10];			

char path_cmd[50];		

int path_sum;			

char work_path[100];	
	




int jobs_count=0;		

pid_t pid;				

char *argv[10];			

int cmd_count=0;		

int stop_flag=0;

//-------------------------------------------------------------------------

void print_work_path();						

void print_string(char *);
					
int read_commands(char *);					

void init_envi();							

void analyse_cmd(char**);					

int cmd_addpath(char *);					





void my_cd(char* );							



void my_history(char *);					

void save_history();						








void my_jobs();								

void my_fg(char *ch,sigset_t *son_set);		

void my_bg(char *);
							

void add_node(int,char*,char *);			

void father_sig_hander(int );				

void reDirect(char *buf);					
char *instru_divide(char *ch);
void aru_divide(char *instru, char **ch);

//-----------------main-----------------

main (){

	
	int is_bg;		

	



	

	init_envi();	

		

	//-------------------------

		



		sigset_t son_set;
				
		struct sigaction father_pact;
		father_pact.sa_handler=father_sig_hander;	



		sigemptyset(&son_set);			


		sigaddset(&son_set,SIGTSTP);

		sigaddset(&son_set,SIGINT);
	
	

	//-------------------------

	while(1){

		sigprocmask(SIG_BLOCK,&son_set,NULL);
		//pid=0;
		stop_flag=0;
		waitpid(-1,NULL,WNOHANG);		

		


		signal(SIGINT,father_sig_hander);
		
		sigaction(SIGTSTP,&father_pact,NULL);			
			
		

		
		is_bg=0;	

		
		print_work_path();		

		

		if (!read_commands(buf))	

			continue;

		

		save_history(his);	

		

	
		int flag=cmd_match(buf);
		if(flag==1){



//-----------------

				

			reDirect(buf);
			continue;

		}		

		else if(flag==2){ 	

			is_bg=1; 
			

		}

		

				

		analyse_cmd(argv);


				

	//------------------------------------------------

				if(!strcmp("exit",argv[0])&&argv[1]==NULL)

				{	puts("goodbye!");exit(0);}

				else if(!strcmp("cd",argv[0])){			//------cd

					my_cd(argv[1]);

					continue;

				}

				else if(!strcmp("history",argv[0])){	//-------history

					my_history(argv[1]);

					continue;

				}

				else if(!strcmp("jobs",argv[0])){		//--------jobs

					my_jobs();

					continue;

				}

				else if(!strcmp("fg",argv[0])){			//--------fg

					my_fg(argv[1],&son_set);

					continue;

				}

				else if(!strcmp("bg",argv[0])){			//--------bg

					my_bg(argv[1]);

					continue;

				}
							

		

		//----------------

				if(cmd_addpath(argv[0])==0){

						

					strcpy(path_cmd,argv[0]);	

												//

				}

				

	//---------------------------------

			



				if((pid=fork())<0)

					perror("execute fail!!");
			
				

		//------------child----------

				else if(pid==0)

				{	

														
					if(execv(path_cmd,argv)==-1)						

							puts("cmd not found!!");
					
		



				}



		//------------parent-------------

				else

				{	

					sigprocmask(SIG_UNBLOCK,&son_set,NULL);

					if(is_bg==1)

						add_node(pid,argv[0],"running");

					else{ 

						

						waitpid(pid,NULL,0);

											

					}

				}

	

			}//while



		

}

//-------------main end-------------------------






//--------------init envp----------------------

void init_envi(){

	FILE *fp;

	char ch,temp[100],*p;

	int i=0;

	his.end=his.start=0;
	jobs_count=0;

	stop_flag=0;

	head=end=NULL;
/*
	if((fp=fopen("./test_profile","r"))==0)

    	{

        	 perror("init path failure!! exit!!");

         	exit(1);

   	 }
   	 */

	while((ch=fgetc(fp))!=EOF)	temp[i++]=ch;
	

		temp[i]=0;	
	

		i=0;

	while(temp[i]&&temp[i]!='=') ++i;
	

	strcpy(temp,temp+i+1);		


	char *delim = ":";

	p=strtok(temp, delim);


	path[0]=(char *)malloc(strlen(p)+1);
	

	strcpy(path[0],p);
	

	i=1;

	while((p=strtok( NULL, delim))!=NULL) 

      { 	path[i]=(char *)malloc(strlen(p)+1);
		

			strcpy(path[i],p);		
		

			i++;
	

		}
	

		path_sum=i-1;


}





//------------cmd match--------------------------------------------------------

int cmd_match(char *buf){

	char *ch;

	if(strchr( buf, '|')!=NULL||strchr( buf, '<')!=NULL||strchr( buf, '>')!=NULL)

		return 1;

	else if((ch=strchr( buf, '&'))!=NULL)

	{	*ch=0 ; return 2;}

	

}









//--------------------------------------------

void print_work_path() {

	char * user = getlogin();

	getcwd(work_path,sizeof(work_path));
		

	printf("<");

	print_string(user);

	printf("@ ");

	print_string(work_path);

	printf(">$");

}







//------------------------------------

int  read_commands(char * buf)

{

	int i=0;char ch;

	
//printf("%s",buf);

	//gets(buf);


	ch=getchar();
	

	if(ch=='\n') return 0;

	buf[i++]=ch;
	while((ch=getchar())!='\n') buf[i++]=ch;
	buf[i]=0;

//puts(buf);
	i=0;

	while(buf[i]==' '||buf[i]=='\t'&&buf[i]) ++i;

	if(buf[i]==0) 
		return 0;
		
	

	else {
		i=0;

		while(buf[i]==' '||buf[i]=='\t') ++i;//remove space

		strcpy(buf,buf+i);		
		
//		int j=strlen(buf)-1;
//		while(buf[j]==' '||buf[j]=='\t') j--;
//		buf[j+1]=0;
//puts(buf);
//printf("%d",strlen(buf));
		return 1;

	}

}









//----------------------------------------

void analyse_cmd(char **argv){

	int i=1;

	char *delim = "' ','\t'";

	argv[0]=strtok(buf, delim);
//puts(argv[0]);

	while((argv[i]=strtok( NULL, delim))!=NULL) 

      		i++;

    

	argv[i]=NULL;





}









//--------------------------------------

int cmd_addpath(char * argv){

	int i=0;

	//char 
//puts("!!!!");

	
//printf("%d\n",path_sum);
	strcpy(path_cmd,work_path);//check current path
	
	strcat(path_cmd,"/");
	
		strcat(path_cmd,argv);

//puts(path_cmd);
	
	if(!access(path_cmd,F_OK))
		
		return 1;
	while(i<path_sum){		//check path directory

		strcpy(path_cmd,path[i]);

		strcat(path_cmd,"/");


		
		

		strcat(path_cmd,argv);
//puts(path_cmd);

		if(access(path_cmd,F_OK)==0)

			return 1;
		i++;

	}




	return 0;

}









//---------------------------

void print_string(char *p){


	printf("%s",p);



}







//--------------------------------------------------------



//------------save history------------------


void save_history(){

	

	cmd_count++;

	his.start%=HIS_LEN;

	his.end%=HIS_LEN;							

	strcpy(his.cmd[his.end++],buf);
	

	if((his.end)%HIS_LEN==his.start)
		

		his.start++;	


}











//---------------------cd-------------------------

void my_cd(char * para){

	
	if(para==NULL)

			chdir("/root");
	

		else if(para!=NULL&&chdir(para)==-1)
		

			perror("dir not exist!!n");




}









//----------------------history----------------------------

void my_history(char * argv) {

	int i=0,len,j;

	if(cmd_count<HIS_LEN) len=cmd_count;

	else	len=HIS_LEN;
	

	j=len;

	if(argv==NULL){
		

		for(i=his.start;i<his.start+len;i++)

		printf("%d \t%s\n",j--,his.cmd[i%HIS_LEN]);


	}

	

}









//------------------------------------------------


void my_jobs()

{	int i=1;

	if(head==NULL) {printf("no jobs!!\n");return;}
	node *p=head;

	while(p->next!=NULL)

	{

		printf("[%d]- %d  %s \t%s\n",i++,p->pid,p->state,p->cmd);

		p=p->next;

	}

	printf("[%d]+ %d  %s  \t%s\n",i,p->pid,p->state,p->cmd);

}



//-------------------------fg-----------------------



void my_fg(char *ch,sigset_t *son_set){
	
	sigprocmask(SIG_UNBLOCK,son_set,NULL);

	if(head==NULL){					//
		printf("no  job!!\n");
		return;
	
	}
	if(ch==NULL){			//
		puts("para missing!");
	}
	else {				//

		int i=atoi(ch),j=1;
		

		node *p=head,*q;

		

if(i>2){
			while(p->next!=NULL&&j<i-1)
					{	p=p->next; j++;}

			if(p->next==NULL) {
				printf("no such job!!\n");
				return;
			}
			
		}else {
				
			if(i==2&&p->next==NULL)
				{printf("no such job!!\n");return;}

		}
		
		if(i==1){
				if(head==end){
					
					//printf("only one!\n");
					
					p=head;		
				
					pid=p->pid;printf("%d\t\t%s\n",pid,p->cmd);
					free(p);
					head=end=NULL;
				}else{
				//
printf("i==1!\n");		
				q=p->next;
				pid=head->pid;printf("%d\t\t%s\n",pid,head->cmd);
				free(head);
				head=q;}
		}					
		else{

			
		//printf("other\n");
			q=p->next;
			pid=q->pid;
			p->next=q->next;
			if(q->next==NULL) end=p;//如果是最后一个结点
			printf("%d\t\t%s\n",pid,q->cmd);
			free(q);

		}

		jobs_count--;
		kill(pid,SIGCONT);
		waitpid(pid,NULL,0);

						

	}

	

	

}





//-------------------------bg---------------------



void my_bg(char *ch){

	if(head==NULL){					//no work

		printf("no  job!!\n");

		return;

	

	}

	else if(ch==NULL){				//put job to bg

		puts("para missing!");

	}

	else{							//put jobs to bg with number



		int i=atoi(ch),j=1;

		node *p=head;

		while(p->next!=NULL&&j++<i)	p=p->next; 
		if(p==NULL) {
			printf("no such job!!\n");
			return;
		}
		pid=p->pid;
		
		kill (pid,SIGCONT);
		strcpy(p->state,"running");
	
	

	}




}




//------------------------------------

void add_node(int pid,char *argv,char *state)

{

	

	node *p;

	jobs_count++;

	p=(node *)malloc(sizeof(node));

	p->pid=pid;

	if (stop_flag==0)

		strcat(argv," &");

	strcpy(p->cmd,argv);

    strcpy(p->state,state);
	

	p->next=NULL;
	

	

	printf("[%d] %d  %s  %s\n",jobs_count,p->pid,p->state,p->cmd);

	

	if(head==NULL){

		head=p;end=p;

	}else {

		end->next=p;

		end=end->next;

	}





}







//---------------------------------------------------

void father_sig_hander(int p){


	if(p==SIGINT)

	{	
		kill(pid,SIGTERM);					

	}

	else if(p==SIGTSTP)

	{

		stop_flag=1;
//printf("%d\n",pid);

		kill(pid,SIGSTOP);
		
		add_node(pid,strrchr(path_cmd,'/')+1,"stop");

	}

} 







//----------------------------------------------//

//-------------*-------------------------/
char *instru_divide(char *ch){
	char *argu1, *argu2;
	char *token[] = {"<",">","|"};
	int i = 0, len;
  len = strlen(ch);
	for(;i<3;i++){
		argu1 = strtok(ch,token[i]);
		if(strlen(argu1)<len){
        break;
		}
		else{
		  continue;
		}
	}
	return token[i];
}


//--------------------------/
void aru_divide(char *instru, char **ch){
	int i = 0;
	ch[i] = strtok(instru," ");
	while(ch[i]&&i<8){
		i++;
		ch[i] = strtok(NULL," ");
	}
	ch[i] = (char *)0;//
}

void reDirect(char *str){
	char *instru1 , *instru2 , *token , ch[50];
	int pipe_fd[2];
  int fid;
  strcpy(ch,str);
	token = instru_divide(str);
  instru1 = strtok(str,token);
  instru2 = strstr(ch,token)+1;
	char *prog1[8],*prog2[8];
  aru_divide(instru1,prog1);
  aru_divide(instru2,prog2);
	pid_t pid[2];
	if(strcmp(token,"|")==0){
		if(pipe(pipe_fd) < 0){
			perror ("pipe failed");
			exit (errno);            
		}

		pid[0] = fork();
		if(pid[0]<0){
			perror("fork error");
			exit(1);
		}

		else if(pid[0]==0){
			 close(pipe_fd[0]);
			dup2(pipe_fd[1], 1);
			close(pipe_fd[1]);
			execvp(prog1[0], prog1);
		}

		else{
	  	pid[1] = fork();
			if(pid[1]<0){
				  perror("fork error");
				  exit(1);
			  }
			else if(pid[1]==0){
				close(pipe_fd[1]);
				dup2(pipe_fd[0],0);
				close(pipe_fd[0]);
				if(execvp(prog2[0], prog2)){
					close(pipe_fd[0]);
					close(pipe_fd[1]);
					return;
				}
			}
			close(pipe_fd[0]);
			close(pipe_fd[1]);
			waitpid(pid[0],NULL,0);
			waitpid(pid[1],NULL,0);

	  }
	  return;
	}
  if(strcmp(token,">")==0){
  	if((fid = open(prog2[0],O_CREAT|O_RDWR|O_TRUNC))==-1){
  		perror("open error");
  		exit(1);
  	}
  	pid[0] = fork();
  	if(pid[0]<0){
  		perror("fork error");
  		exit(1);
  	}
  	else if(pid[0]==0){
  		dup2(fid,1);
  		close(fid);
  		execvp(prog1[0], prog1);
  	}
  	else{
  		waitpid(pid[0],NULL,0);
  	}
  	return;
	} 
	if(strcmp(token,"<")==0){
		if(access(prog2[0],F_OK)){
			perror("file not found");
			exit(1);
		}
		pid[0] = fork();
		if(pid[0]<0){
			perror("fork error");
			exit(1);
		}
	  else if(pid[0]==0){
	  	fid = open(prog2[0],O_RDWR);
	  	dup2(fid,0);
	  	close(fid);
	  	if(execvp(prog1[0], prog1)){
	  		puts("rwewrw");
	  	}
	  }
	  else{
	  	waitpid(pid[0],NULL,0);
	  }
	  return;
	}
}
